package com.derbysoft.dswitch.adapter.ihg.dswitch2.translate;

import com.derbysoft.dswitch.adapter.ihg.common.utils.DateTimeUtils;
import com.derbysoft.dswitch.adapter.ihg.dswitch3.translate.Translator;
import com.derbysoft.dswitch.adapter.ihg.dto.OTAHotelAvailRS;
import com.derbysoft.dswitch.adapter.ihg.dto.Rate;
import com.derbysoft.dswitch.adapter.ihg.dto.RoomRate;
import com.derbysoft.dswitch.adapter.ihg.dto.TimeSpan;
import com.derbysoft.remote.dswitch.core.*;
import com.derbysoft.remote.dswitch.generic.constants.TransactionStatusType;
import com.derbysoft.remote.dswitch.generic.dto.SimpleAmountDTO;
import com.derbysoft.remote.dswitch.generic.dto.UniqueIDDTO;
import com.derbysoft.remote.dswitch.hotel.dto.*;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Date;

@Component("ds2HotelPreviewRSTranslator")
public class HotelPreviewRSTranslator extends BaseTranslator
        implements Translator<OTAHotelAvailRS, GenericRS<HotelReservationRS>, GenericRQ<HotelReservationRQ>> {

    @Override
    public GenericRS<HotelReservationRS> translate(OTAHotelAvailRS otaHotelAvailRS, GenericRQ<HotelReservationRQ> request) {
        ResponseHeader responseHeader = new ResponseHeader();
        responseHeader.setTaskId(request.getHeader().getTaskId());
        GenericRS<HotelReservationRS> hotelReservationRS = new GenericRS<HotelReservationRS>();
        hotelReservationRS.setHeader(responseHeader);
        HotelReservationRS reservationRS = translateHotelReservationRS(otaHotelAvailRS, request);
        if (reservationRS == null) {
            return failedReservationRS(request);
        }
        hotelReservationRS.setSuccess(new SuccessDTO());
        hotelReservationRS.setPayload(reservationRS);
        return hotelReservationRS;
    }

    private GenericRS<HotelReservationRS> failedReservationRS(GenericRQ<HotelReservationRQ> request) {
        ErrorDTO error = new ErrorDTO();
        error.setCode(SYSTEM);
        error.setSource(adapter);
        String ratePlan = request.getPayload().getHotelReservation().getRoomStays().get(0).getRatePlanCode();
        String roomType = request.getPayload().getHotelReservation().getRoomStays().get(0).getRoomTypeCode();
        int numberOfUnits = request.getPayload().getHotelReservation().getRoomStays().get(0).getNumberOfUnits();
        error.setMessage(String.format("No allotment for ratePlanCode:[%s] , roomTypeCode:[%s] numberOfUnits:[%d]", ratePlan, roomType, numberOfUnits));
        ResponseHeader header = new ResponseHeader();
        header.setTaskId(request.getHeader().getTaskId());
        ErrorsDTO errors = new ErrorsDTO();
        errors.getErrorList().add(error);
        GenericRS<HotelReservationRS> genericRS = new GenericRS<HotelReservationRS>();
        genericRS.setHeader(header);
        genericRS.setErrors(errors);
        return genericRS;
    }

    private HotelReservationRS translateHotelReservationRS(OTAHotelAvailRS otaHotelAvailRS, GenericRQ<HotelReservationRQ> request) {
        HotelReservationRQ hotelReservationDTO = request.getPayload();
        HotelReservationRS hotelReservationRS = new HotelReservationRS();
        hotelReservationRS.setHotelReservationIDs(hotelReservationDTO.getHotelReservationIDs());
        hotelReservationRS.setOriginalBuyer(hotelReservationDTO.getOriginalBuyer());
        hotelReservationRS.setOriginalProvider(translateOriginalProvider(request));
        hotelReservationRS.setResStatusType(TransactionStatusType.INITIATED);
        HotelReservationDTO hotelReservationRQ = translateHotelReservation(hotelReservationDTO, otaHotelAvailRS);
        if (hotelReservationRQ == null) {
            return null;
        }
        hotelReservationRS.setHotelReservation(hotelReservationRQ);
        hotelReservationRS.setTotal(hotelReservationRS.getHotelReservation().getRoomStays().get(0).getTotal());
        return hotelReservationRS;
    }

    private HotelReservationDTO translateHotelReservation(HotelReservationRQ hotelReservationRQ,
                                                          OTAHotelAvailRS otaHotelAvailRS) {
        HotelReservationDTO hotelReservationDTO =
                (HotelReservationDTO) hotelReservationRQ.getHotelReservation().clone();
        ReservationRoomStayDTO reservationRoomStayDTO = hotelReservationDTO.getRoomStays().get(0);
        RoomRate roomRate = findRoomRate(otaHotelAvailRS, reservationRoomStayDTO);
        if (roomRate == null) {
            return null;
        }
        RoomRateDTO roomRateDTO = findRoomRateDTO(roomRate, reservationRoomStayDTO);
        reservationRoomStayDTO.setRoomRate(roomRateDTO);
        calcTotal(roomRate, reservationRoomStayDTO, reservationRoomStayDTO.getStayDateRange());
        return hotelReservationDTO;
    }

    private void calcTotal(RoomRate roomRate, ReservationRoomStayDTO reservationRoomStayDTO, StayDateRangeDTO stayDateRangeDTO) {
        BigDecimal amount = BigDecimal.ZERO;
        for (RateDTO rateDTO : reservationRoomStayDTO.getRoomRate().getRates()) {
            amount = amount.add(rateDTO.getPureAmount().getAmount().multiply(BigDecimal.valueOf(rateDTO.getDateRange().length())));
        }
        Rate ihgRate = roomRate.getRates().getRate().get(0);
        Date startDate = stayDateRangeDTO.getCheckInDate();
        Date endDate = DateTimeUtils.addDays(stayDateRangeDTO.getCheckOutDate(), -1);
        TotalDTO total = new TotalDTO();
        total.setTaxes(translateTaxes(ihgRate, startDate, endDate));
        SimpleAmountDTO pureAmount = new SimpleAmountDTO();
        pureAmount.setCurrency(reservationRoomStayDTO.getRoomRate().getRates().get(0).getPureAmount().getCurrency());
        pureAmount.setAmount(amount);
        total.setPureAmount(pureAmount);
        total.setTotalAmount(pureAmount);
        reservationRoomStayDTO.setTotal(total);
    }

    private RoomRateDTO findRoomRateDTO(RoomRate roomRate, ReservationRoomStayDTO reservationRoomStayDTO) {
        TimeSpan timeSpan = new TimeSpan();
        timeSpan.setStart(DateTimeUtils.formatDate(reservationRoomStayDTO.getStayDateRange().getCheckInDate()));
        timeSpan.setEnd(DateTimeUtils.formatDate(reservationRoomStayDTO.getStayDateRange().getCheckOutDate()));
        return translateRoomRate(roomRate, timeSpan);
    }

    private RoomRate findRoomRate(OTAHotelAvailRS otaHotelAvailRS, ReservationRoomStayDTO reservationRoomStayDTO) {
        TimeSpan timeSpan = new TimeSpan();
        timeSpan.setStart(DateTimeUtils.formatDate(reservationRoomStayDTO.getStayDateRange().getCheckInDate()));
        timeSpan.setEnd(DateTimeUtils.formatDate(reservationRoomStayDTO.getStayDateRange().getCheckOutDate()));
        String ratePlanCode = reservationRoomStayDTO.getRatePlanCode();
        String roomTypeCode = reservationRoomStayDTO.getRoomTypeCode() + ratePlanCode;
        for (RoomRate roomRate : otaHotelAvailRS.getRoomStays().getRoomStay().get(0).getRoomRates().getRoomRate()) {
            if (roomRate.getRatePlanCode().equals(ratePlanCode) && roomRate.getRoomTypeCode().equals(roomTypeCode)) {
                return roomRate;
            }
        }
        return null;
    }

    private UniqueIDDTO translateOriginalProvider(GenericRQ<HotelReservationRQ> request) {
        return request.getHeader().getDestinations().get(0).getUniqueID();
    }
}
